#include "BSP.h"
#include <string.h>
#include "mg_api.h"

__align(2) static uint8_t Tab[33];

LPTIM_HandleTypeDef LPTIMCONF = {0};
static unsigned char *ble_mac_addr0;

void SPI_DMA_Init(void)
{
    HAL_SYSCFG_DMA_Req(1);
    HAL_SYSCFG_DMA_Req(0x200);

    SET_BIT(SPI1->CR2, SPI_RXFIFO_THRESHOLD);

    DMA1_Channel2->CCR &= ~DMA_CCR_EN;

    DMA1->IFCR = 1 << 4;

    DMA1_Channel2->CNDTR = 0;

    DMA1_Channel2->CPAR = (u32) & (SPI1->DR);
    DMA1_Channel2->CMAR = (uint32_t)Tab;

    DMA1_Channel2->CCR |= (DMA_IT_TC | DMA_IT_HT | DMA_IT_TE);
    DMA1_Channel2->CCR |= DMA_CCR_EN;

    SET_BIT(SPI1->CR2, SPI_CR2_RXDMAEN);

    DMA1_Channel1->CCR &= ~DMA_CCR_EN;

    DMA1->IFCR = 1;

    DMA1_Channel1->CNDTR = 0;

    DMA1_Channel1->CPAR = (u32) & (SPI1->DR);
    DMA1_Channel1->CMAR = (uint32_t)Tab;

    DMA1_Channel1->CCR &= ~(DMA_IT_HT);
    DMA1_Channel1->CCR |= (DMA_IT_TC | DMA_IT_TE);

    DMA1_Channel1->CCR |= DMA_CCR_EN;

    SPI1->CR2 |= SPI_IT_ERR;
    SET_BIT(SPI1->CR2, SPI_CR2_TXDMAEN);
}

void SPI_CS_Enable_(void)
{
    SPI_NSS_GPIO->BRR = SPI_NSS_PIN;
}

void SPI_CS_Disable_(void)
{
    SPI_NSS_GPIO->BSRR = SPI_NSS_PIN;
}

void SPI_init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    CLEAR_BIT(SPI1->CR1, SPI_CR1_SPE);
    __HAL_RCC_SPI1_FORCE_RESET();
    __HAL_RCC_SPI1_RELEASE_RESET();

    // enable all gpio clock
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();

    __HAL_RCC_SPI1_CLK_ENABLE();
    __HAL_RCC_DMA_CLK_ENABLE();

    // SPI CS
    GPIO_InitStruct.Pin = SPI_NSS_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    HAL_GPIO_Init(SPI_NSS_GPIO, &GPIO_InitStruct);

    SPI_CS_Disable_();

    GPIO_InitStruct.Pin = SPI_CLK_PIN;
    GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = SPI_CLK_AF;
    HAL_GPIO_Init(SPI_CLK_GPIO, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = SPI_MOSI_PIN;
    GPIO_InitStruct.Alternate = SPI_MOSI_AF;
    HAL_GPIO_Init(SPI_MOSI_GPIO, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = SPI_MISO_PIN;
    GPIO_InitStruct.Alternate = SPI_MISO_AF;
    HAL_GPIO_Init(SPI_MISO_GPIO, &GPIO_InitStruct);

    HAL_NVIC_SetPriority(SPI1_IRQn, 1, 0);
    HAL_NVIC_EnableIRQ(SPI1_IRQn);
    DMA1_Channel1->CCR;
    DMA1_Channel1->CCR = 0x00003090;
    DMA1_Channel2->CCR;
    DMA1_Channel2->CCR = 0x00002080;

    DMA1_Channel1->CCR &= ~(3 << 12);
    DMA1_Channel1->CCR |= (0 << 12);
    DMA1_Channel2->CCR &= ~(3 << 12);
    DMA1_Channel2->CCR |= (3 << 12);

    HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 1, 1);
    HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);

    HAL_NVIC_SetPriority(DMA1_Channel2_3_IRQn, 1, 1);
    HAL_NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);

    CLEAR_BIT(SPI1->CR1, SPI_CR1_SPE);

    SPI1->CR1 = 0x0000030c;
    SPI1->CR2 = 0x00001700;

    SPI_DMA_Init();

    SPI_CS_Disable_();
}

unsigned char SPI_WriteBuf(unsigned char reg, unsigned char const *pBuf, unsigned char len)
{
    uint32_t i;

    SPI_CS_Enable_();

    DMA1_Channel1->CCR &= ~DMA_CCR_EN;
    DMA1_Channel2->CCR &= ~DMA_CCR_EN;

    DMA1->IFCR = 0x00000011;

    Tab[0] = reg;
    memcpy(Tab + 1, pBuf, len);

    DMA1_Channel1->CNDTR = len + 1;
    DMA1_Channel2->CNDTR = len + 1;

    DMA1_Channel1->CCR |= DMA_CCR_EN;
    DMA1_Channel2->CCR |= DMA_CCR_EN;

    SET_BIT(SPI1->CR1, SPI_CR1_SPE);

    // empirical value,will be optimized,may need to be adjusted
    i = (len << 3) + 8;
    while (i--)
        ;

    SPI_CS_Disable_();

    return 0;
}

unsigned char SPI_ReadBuf(unsigned char reg, unsigned char *pBuf, unsigned char len)
{
    uint32_t i;

    SPI_CS_Enable_();

    DMA1_Channel1->CCR &= ~DMA_CCR_EN;
    DMA1_Channel2->CCR &= ~DMA_CCR_EN;

    DMA1->IFCR = 0x00000011;

    Tab[0] = reg;

    DMA1_Channel1->CNDTR = len + 1;
    DMA1_Channel2->CNDTR = len + 1;

    DMA1_Channel1->CCR |= DMA_CCR_EN;
    DMA1_Channel2->CCR |= DMA_CCR_EN;

    SET_BIT(SPI1->CR1, SPI_CR1_SPE);

    // empirical value,will be optimized,may need to be adjusted
    i = (len << 3) + 8;
    while (i--)
        ;

    SPI_CS_Disable_();

    memcpy(pBuf, Tab + 1, len);

    return 0;
}

void Configure_EXTI(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Pin = IRQ_PIN;
    HAL_GPIO_Init(IRQ_GPIO, &GPIO_InitStruct);

    HAL_NVIC_EnableIRQ(IRQ_EXTNUMBER);
    HAL_NVIC_SetPriority(IRQ_EXTNUMBER, 0, 0);
}

static unsigned char TimeOut = 0;
void LPTIM_TimeOut_Reset(void)
{
    TimeOut = 0;
}

void LPTIM1_IRQHandler(void)
{
    __HAL_LPTIM_CLEAR_FLAG(&LPTIMCONF, LPTIM_IT_ARRM);

    HAL_LPTIM_ReadCounter(&LPTIMCONF); // 回读重置计数器
    // while (0 != HAL_LPTIM_ReadCounter(&LPTIMCONF))
    //     ;
    // while (0 != HAL_LPTIM_ReadCounter(&LPTIMCONF))
    //     ;
    __HAL_LPTIM_START_SINGLE(&LPTIMCONF);

    if (3 == TimeOut)
    {
        HAL_NVIC_DisableIRQ(IRQ_EXTNUMBER);
        radio_initBle(TXPWR_0DBM, &ble_mac_addr0);
        HAL_NVIC_EnableIRQ(IRQ_EXTNUMBER);
        ble_run_interrupt_start(0);
    }
    if (u8RunningModeStat != dfRunningMode_Standby)
        TimeOut++;
}
/********************************************************************************************************
**函数信息 ：使能LSI时钟，配置LPTIM时钟
**功能描述 ：时钟配置
**输入参数 ：
**输出参数 ：
**    备注 ：
********************************************************************************************************/
static void APP_RCC_OscConfig(void)
{
    RCC_OscInitTypeDef OSCINIT;
    RCC_PeriphCLKInitTypeDef LPTIM_RCC;

    OSCINIT.OscillatorType = RCC_OSCILLATORTYPE_LSI;
    OSCINIT.LSIState = RCC_LSI_ON; // LSI开启

    LPTIM_RCC.PeriphClockSelection = RCC_PERIPHCLK_LPTIM;
    LPTIM_RCC.LptimClockSelection = RCC_LPTIMCLKSOURCE_LSI; // 选用LSI作为LPTIM的时钟源

    if (HAL_RCCEx_PeriphCLKConfig(&LPTIM_RCC) != HAL_OK) // 外设时钟初始化
    {
        Error_Handler();
    }

    if (HAL_RCC_OscConfig(&OSCINIT) != HAL_OK) // 时钟初始化
    {
        Error_Handler();
    }
    __HAL_RCC_LPTIM_CLK_ENABLE(); // 使能LPTIM时钟
}
/********************************************************************************************************
**函数信息 ：初始化LPTIM
**功能描述 ：主函数
**输入参数 ：
**输出参数 ：
**    备注 ：
********************************************************************************************************/
static void APP_LPTIM_INIT(void)
{
    LPTIMCONF.Instance = LPTIM;
    LPTIMCONF.Init.Prescaler = LPTIM_PRESCALER_DIV32;     // LSI 32分频
    LPTIMCONF.Init.UpdateMode = LPTIM_UPDATE_ENDOFPERIOD; //

    if (HAL_LPTIM_Init(&LPTIMCONF) != HAL_OK)
    {
        Error_Handler();
    }
    __HAL_LPTIM_RESET_COUNTER_AFTERREAD(&LPTIMCONF); // 使能读写计数器时重置计数器
}
/********************************************************************************************************
**函数信息 ：使能LPTIM
**功能描述 ：主函数
**输入参数 ：
**输出参数 ：
**    备注 ：
********************************************************************************************************/
static void APP_LPTIM_IT(void)
{
    if (HAL_LPTIM_SetOnce_Start_IT(&LPTIMCONF, 0x400) != HAL_OK) // 1024/(32768/32)=1s
    {
        Error_Handler();
    }
    HAL_NVIC_SetPriority(LPTIM1_IRQn, 0x02, 0); // 设置LPTIM中断优先级
    HAL_NVIC_EnableIRQ(LPTIM1_IRQn);
}
void LPTIM_Init(void)
{
    // 时钟设置
    APP_RCC_OscConfig();
    // LPTIM初始化
    APP_LPTIM_INIT();
    // 开启LPTIM中断
    APP_LPTIM_IT();
}
char IsIrqEnabled(void)
{
    return (!HAL_GPIO_ReadPin(IRQ_GPIO, IRQ_PIN));
}
void McuGotoSleepAndWakeup(void)
{
}

void IrqMcuGotoSleepAndWakeup(void)
{
    static u32 LowPowerDelay = 0x00;

    if (GetUartStatus())
    {
        LowPowerDelay = GetSysTickCount();
        return; // 串口有数据待处理，不进入判断低功耗
    }
    if (GetKeyStatus())
    {
        LowPowerDelay = GetSysTickCount();
        return; // 按键按下操作
    }

    if ((GetSysTickCount() - LowPowerDelay) < 50)
        return; // 进入低功耗延时
    else
        LowPowerDelay = GetSysTickCount() - 50;

    switch (u8RunningModeSet)
    {
    case dfRunningMode_Sleep:
    { // MCU休眠，蓝牙工作
        if (u8RunningModeStat == dfRunningMode_Standby)
            radio_resume();

        if (ble_run_interrupt_McuCanSleep())
        {
            u8RunningModeStat = dfRunningMode_Sleep;
            CLEAR_BIT(SPI1->CR1, SPI_CR1_SPE);
            HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); // stop mode
            SET_BIT(SPI1->CR1, SPI_CR1_SPE);
        }
    }
    break;
    case dfRunningMode_Standby:
    { // 整机休眠
        if (ble_run_interrupt_McuCanSleep())
        {
            if (u8RunningModeStat != dfRunningMode_Standby)
                radio_standby();

            u8RunningModeStat = dfRunningMode_Standby;
            CLEAR_BIT(SPI1->CR1, SPI_CR1_SPE);
            LED_SetStatus(TRUE);
            HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); // stop mode
            SET_BIT(SPI1->CR1, SPI_CR1_SPE);
        }
    }
    break;
    default: // dfRunningMode_Normal
    {
        if (u8RunningModeStat == dfRunningMode_Standby)
            radio_resume();

        u8RunningModeStat = dfRunningMode_Normal;
        break;
    }
    }
}
void SysClk8to48(void)
{
}
void SysClk48to8(void)
{
}
void DisableEnvINT(void)
{
}
void EnableEnvINT(void)
{
}
void UpdateLEDValueAll(void)
{
}
__weak void OtaSystemReboot(void)
{
}
__weak u32 GetOtaAddr(void)
{
    return 0;
}
__weak void WriteFlashE2PROM(u8 *data, u16 len, u32 pos, u8 flag)
{
}
